﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Licenta1.Expressions;

namespace Licenta1.Statements
{
    class FunctionStatement : Statement
    {
        string f_name;
        Statement statements;
        ParameterList plist;
        
        public FunctionStatement(Context con, Expression name, Statement list, Expression p)
            : base(con)
        {
            f_name = ((IdentifierExpression)name).Name;
            statements = list;
            plist = (ParameterList)p;
        }

        public override void Execute()
        {
            foreach (UserFunction function in context.userFunctions)
                if (function.name == f_name)
                {
                    Console.WriteLine("A duplicate name for function already exists.");
                    return;
                }
            context.userFunctions.Add(new UserFunction(f_name, statements, plist));
        }

        public string NAME
        {
            get { return f_name; }
        }

        public Statement STATEMENTS
        {
            get { return statements; }
        }

        public ParameterList PARAMETERS
        {
            get { return plist; }
        }
    }

    class UserFunction
    {
        public string name;
        public Statement s;
        public List<string> param;

        public UserFunction(string f_name, Statement st, ParameterList p)
        {
            name = f_name;
            s = st;
            param = new List<string>();
            PopulateParam(p);
        }

        public void PopulateParam(ParameterList p)
        {
            if (p != null)
            {
                try
                {
                    if (p.FirstValue != null)
                        param.Add(((ObjectMathExpressionID)p.currentP).val.Name);
                    if (p.SecondValue != null)
                        if (p.nextP.GetType() == typeof(ObjectMathExpressionID))
                            param.Add(((ObjectMathExpressionID)p.nextP).val.Name);
                        else if (p.SecondValue.GetType() == typeof(ParameterList))
                            PopulateParam((ParameterList)p.SecondValue);
                }
                catch (InvalidCastException e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }

    class FunctionExecution 
    {
        UserFunction f;
//        Context c;
        Hashtable localVariables, parameters;
        ArrayList collections, functions;
        //object returningObject;

        public FunctionExecution(UserFunction f, Expression p)
        {
            this.f = f;
            parameters = new Hashtable();
            localVariables = new Hashtable();
            collections = new ArrayList();
            functions = new ArrayList();

            PopulateParams((ParameterList)p);
        }

        private void PopulateParams(ParameterList p)
        {
            try
            {
                if (p.FirstValue != null)
                {
                    parameters.Add(f.param.ElementAt(0), p.FirstValue);
                    f.param.RemoveAt(0);
                }
                if (p.SecondValue != null)
                    if (p.SecondValue.GetType() != typeof(ParameterList))
                    {
                        parameters.Add(f.param.ElementAt(0), p.SecondValue);
                        f.param.RemoveAt(0);
                    }
                    else PopulateParams((ParameterList)p.SecondValue);
            }
            catch (InvalidCastException e)
            {
                Console.WriteLine(e.Message);
            }
        }


        public object Value()
        {
            Statement s = f.s;

            while (s.GetType() == typeof(StatementList))
            {
                Solve(((StatementList)s).currentS);
                s = ((StatementList)s).nextS;
            }
            Solve(s);

            return 7;
        }

        private void Solve(Statement s)
        {
            if (s.GetType() == typeof(AssignmentStatement))
            {
                TreatAssignment(((AssignmentStatement)s).IDENTIFIER, ((AssignmentStatement)s).EXPRESSION);
            }

            if (s.GetType() == typeof(CollectionAddStatement))
            {
                TreatCollectionAdd(((CollectionAddStatement)s).NAME, ((CollectionAddStatement)s).OBJECT);
            }

            if (s.GetType() == typeof(CollectionRemoveStatement))
            {
                TreatCollectionRemove(((CollectionRemoveStatement)s).NAME, ((CollectionRemoveStatement)s).OBJECT);
            }

            if (s.GetType() == typeof(CollectionClearStatement))
            {
                TreatCollectionClear(((CollectionClearStatement)s).NAME);
            }

            if (s.GetType() == typeof(CollectionStatement))
            {
                TreatCollection(((CollectionStatement)s).NAME);
            }

            if (s.GetType() == typeof(FunctionStatement))
            {
                TreatFunction(((FunctionStatement)s).NAME, ((FunctionStatement)s).STATEMENTS, ((FunctionStatement)s).PARAMETERS);
            }
        }

        private void TreatFunction(string p, Statement statement, ParameterList parameterList)
        {
            foreach (UserFunction function in functions)
                if (function.name == p)
                {
                    Console.WriteLine("A duplicate name for function already exists.");
                    return;
                }
            functions.Add(new UserFunction(p, statement, parameterList));
        }

        private void TreatCollection(string p)
        {
            foreach (UserCollection uc in collections)
                if (uc.NAME == p)
                {
                    Console.WriteLine("A duplicate name for collection already exists.");
                    return;
                }
            collections.Add(new UserCollection(p));
        }

        private void TreatCollectionClear(string p)
        {
            foreach (UserCollection uc in collections)
                if (uc.NAME == p)
                {
                    uc.Clear();
                    return;
                }
        }

        private void TreatCollectionRemove(string p, Expression expression)
        {
            foreach (UserCollection uc in collections)
                if (uc.NAME == p)
                {
                    uc.Remove(expression);
                    return;
                }
        }

        private void TreatCollectionAdd(string p, Expression expression)
        {
            foreach (UserCollection uc in collections)
                if (uc.NAME == p)
                {
                    uc.Add(expression);
                    return;
                }
        }

        private void TreatAssignment(String id, Expression expr)
        {
            if (parameters.ContainsKey(id))
                parameters[id] = expr.Value;
            else if (localVariables.ContainsKey(id))
                localVariables[id] = expr.Value;
            else localVariables.Add(id, expr.Value);
        }

        private Expression E(Expression e)
        {
            if (e.GetType() == typeof(CollectionIndexExpressions))
            {
                CollectionIndexExpressions c = (CollectionIndexExpressions)e;
                if (Convert.ToInt32(c.INDEX.Value) >= 0 && c.LNAME != "")
                {
                    foreach (UserCollection uc in collections)
                        if (uc.NAME == c.LNAME)
                            return ((Expression)uc.COLLECTION[Convert.ToInt32(c.INDEX.Value)]);
                }
                return null;
            }

            if (e.GetType() == typeof(CollectionFirstExpressions))
            {
                CollectionFirstExpressions f = (CollectionFirstExpressions)e;
                if (f.LNAME != "")
                    foreach (UserCollection uc in collections)
                        if (uc.NAME == f.LNAME)
                            return ((Expression)uc.First());
                return null;
            }

            if (e.GetType() == typeof(CollectionLastExpressions))
            {
                CollectionLastExpressions f = (CollectionLastExpressions)e;
                if (f.LNAME != "")
                    foreach (UserCollection uc in collections)
                        if (uc.NAME == f.LNAME)
                            return ((Expression)uc.Last());
                return null;
            }

            if (e.GetType() == typeof(CollectionSizeExpressions))
            {
                CollectionSizeExpressions f = (CollectionSizeExpressions)e;
                if (f.LNAME != "")
                    foreach (UserCollection uc in collections)
                        if (uc.NAME == f.LNAME)
                            return new NumberExpression(uc.Size());
                return new NumberExpression(0);
 
            }

            if (e.GetType() == typeof(IdentifierExpression))
            {
                IdentifierExpression i = (IdentifierExpression)e;
                if (localVariables.ContainsKey(i.Name))
                    return (Expression)localVariables[i.Name];
                else
                    return null;
            }

            if (e.GetType() == typeof(MathBinaryExpression))
            {
                MathBinaryExpression m = (MathBinaryExpression)e;
                try
                {
                    double m1 = Convert.ToDouble(E(m.E1).Value);
                    double m2 = Convert.ToDouble(E(m.E2).Value);
                    switch (m.SIGN)
                    {
                        case "/": return new NumberExpression(m1 / m2);
                        case "*": return new NumberExpression(m1 * m2);
                        case "+": return new NumberExpression(m1 + m2);
                        case "-": return new NumberExpression(m1 - m2);
                        case "%": return new NumberExpression(m1 % m2);
                        default: return new NumberExpression(0);
                    }
                }
                catch (Exception)
                {
                    return new NumberExpression(0);
                }
            }

            if (e.GetType() == typeof(MathNegativeExpression))
            {
                MathNegativeExpression m = (MathNegativeExpression)e;
                try
                {
                    double m1 = Convert.ToDouble(E(m.E1).Value);
                    return new NumberExpression(-m1);
                }
                catch (Exception)
                {
                    return new NumberExpression(0);
                }
            }

            if (e.GetType() == typeof(ObjectExpression))
            {
                ObjectExpression o = (ObjectExpression)e;
  //              if (o.SHAPE == null)
//                    o.SHAPE = o.createObject(o.NAME, E(o.PARAMETER));

//                return o.SHAPE.Value;
            }

            return e;
        }
    }
}
